import java.math.*;
import java.util.*;
import java.net.*;
import java.io.*;
import java.security.*;
import java.util.concurrent.locks.*;
import org.lsmp.djep.groupJep.groups.Zn;
import org.lsmp.djep.groupJep.values.Polynomial;

public class Player extends Thread{

    //error codes:
    public final int DOUBLESHARERANDOM = 1;
    public final int RECONSPRIV = 2;
    public final int RECONSPUBL = 3;    

    //Field variables
    boolean DEBUG = Utilities.DEBUG; //set this to false in final stage.
    
    int ID, oldID;
    boolean corrupt;
    boolean eliminated = false;
    int unhappyCount = 0;
    SharingProtocols sharingProtocol;    
    HashMap<Integer, Integer> players;
    HashMap<Integer, BigInteger[]> randomDoubleShares; //Holds [r_0]_(d,d')...[r_T]_(d,d') values
    HashMap<Integer, BigInteger> reconsPrivShares;
    HashMap<Integer, BigInteger> reconsPubl_Us;
    HashMap<Integer, BigInteger> compPhaseReconsPriv;
    HashMap<Integer, Boolean> faultDetect;
    HashMap<Integer, Boolean> faultDetectPart2;
    HashMap<Integer, MessageList> messageMap;
    DoubleShareRandom dsr;
    GenerateTriples genTrip;
    boolean happy;
    HIM M;
    MessageList msgs;

    SecureRandom rand;

    //Triples of the form ([a],[b],[c]) - all t-shared where [c]=[a][b]
    ArrayList<BigInteger[]> triples;

    //CircuitController - need to call back to this guy at times.
    CircuitController cc;
    Circuit circuit;

    //net stuff
    DataOutputStream dos;
    int totalBytesSend;
    ObjectOutputStream out;
    ObjectInputStream in;
    OutputStream baseStream; // this is changed when sending. All
                             // other remain the same.
    ArrayList<Socket> sockets;
    PlayerServer server;
    Thread serverThread;

    private final ReentrantLock lock;
    
    Controller main;

    Player(int ID, HIM M, Controller main, CircuitController cc, boolean corrupt){
        this.corrupt = corrupt;
        this.ID = ID;
        oldID = ID;
        this.M = M;        
        rand = new SecureRandom();
        players = new HashMap<Integer, Integer>();
        sharingProtocol = new SharingProtocols(rand, this);        
        randomDoubleShares = null;
        reconsPrivShares = new HashMap<Integer, BigInteger>();
        reconsPubl_Us = new HashMap<Integer, BigInteger>();
        compPhaseReconsPriv = new HashMap<Integer, BigInteger>();
        faultDetect = new HashMap<Integer, Boolean>();
        faultDetectPart2 = new HashMap<Integer, Boolean>();
        messageMap = new HashMap<Integer, MessageList>();

        triples = new ArrayList<BigInteger[]>();
        happy = true;

        this.cc = cc;
        circuit = new Circuit(this);

        lock = new ReentrantLock();

        //net stuff        
        sockets = new ArrayList<Socket>(); //unbound sockets
        for(int i = 0; i<=Utilities.n; i++){
            sockets.add(new Socket());
        }
        server = new PlayerServer(2000+ID, this); //server bound to this ID port
        server.start();

        msgs = new MessageList(ID);

        this.main = main;
    }

    public void run(){}

    public void initPlayer(HashMap<Integer, Integer> players){
        if(players.size() < Utilities.n){
            print("Error - Not enough known players. List has to contain " 
                               + Utilities.n + " players.");
            System.exit(0);
        }
        this.players.putAll(players);
        dsr = new DoubleShareRandom(this, sharingProtocol, M);
        genTrip = new GenerateTriples(this, null);
    }
    
    /**
     * takes a code, an object to send and an id on the player it
     * should be sent to. Then sends it with the code such that server
     * can understand.  The ArrayList of objects is dangerous, but I
     * know what is send, and can handle it as the protocol dictates
     * in the server.
     */
    public void sendMessage(int code, ArrayList<Object> msg, int playerID){
        Utilities.lock.lock();
        Socket s = sockets.remove(playerID);
        try{
            s.connect(new InetSocketAddress("localhost", 2000+playerID));
            
            baseStream = s.getOutputStream();
                
            dos = new DataOutputStream(baseStream);
            out = new ObjectOutputStream(dos);
            out.writeObject(code);
            out.writeObject(ID); //always tag along yourself.
            for(Object o : msg){
                out.writeObject(o);
            }
            out.flush();
            totalBytesSend += dos.size();
        }
        catch(IOException e){
            e.printStackTrace();
        }
        finally{
            try{
                dos.close();
                out.close();
                s.close();
                //replace the old socket with a new one. 
                sockets.add(playerID, new Socket());
            }
            catch(IOException e){
                e.printStackTrace();
            }
            finally{
                Utilities.lock.unlock();
            }
        }
    }

    public int totalBytesSend(){
        return totalBytesSend;
    }

    //========== ReconsPubl ==========

    public void reconsPubl(int degree, HashMap<Integer, BigInteger> shares, int caller, HashMap<Integer, MessageList> msgs){
        Utilities.lock.lock();
        if(msgs != null){
            sharingProtocol.reconsPubl(degree, msgs.get(impersonate).getReconsPubls(), caller, msgs);
        }
        else{
            this.msgs.addReconsPubl(shares);
            sharingProtocol.reconsPubl(degree, shares, caller, msgs);
        }
        Utilities.lock.unlock();
    }

    /**
     * Assumes you are the one who should reconstruct the secret.
     */
    public void receiveReconsPubl(BigInteger share, int degree, int senderID, int caller,
                                  HashMap<Integer, MessageList> msgs){
        Utilities.lock.lock();
        if(reconsPrivShares.containsKey(senderID)){
            //someone calls with an ID that is already there. 
            error(RECONSPRIV);
            Utilities.lock.unlock();
            return;
        }
        if(caller == Utilities.GENTRIP){
            this.msgs.add(share, senderID);
            this.msgs.add(degree, senderID);
        }

        reconsPrivShares.put(senderID, share);
        if(reconsPrivShares.size() == Utilities.n_prime){
            //All shares collected - can now do the actual computation:
            int u_degree;
            if(caller == Utilities.GENTRIP){
                u_degree = 2*Utilities.t_prime;
            }
            else{
                u_degree = Utilities.t;
            }
            BigInteger u_j = sharingProtocol.reconsPriv(u_degree, reconsPrivShares);            

            //Clear the HashMap for next time we need it.
            reconsPrivShares.clear();

            //now send u_j to all other players: 
            ArrayList<Object> list = new ArrayList<Object>();
            if(corrupt)
                list.add(u_j.add(BigInteger.ONE));
            else
                list.add(u_j);
            list.add(degree);
            list.add(caller);
            for(Integer i : players.keySet()){
                if(msgs == null)
                    sendMessage(Utilities.returnReconsPubl, list, i);
                else{
                    //check mode
                    //TODO : finish the rest of the checks.. 
                    MessageList ml = msgs.get(i);
                    int c1 = ml.getMessageCounter(impersonate);
                    BigInteger u = (BigInteger)ml.getNextMsg(impersonate);
                    int c2 = ml.getMessageCounter(impersonate);
                    int d = (Integer)ml.getNextMsg(impersonate);
                    if(!u.equals(u_j)){
                        faultDetected(impersonate, i, u_j, u, c1);
                        Utilities.lock.unlock();
                        return;
                    }
                    else if(d != degree){
                        faultDetected(impersonate, i, degree, d, c1);
                        Utilities.lock.unlock();
                        return;
                    }
                    else{
                        //all is good - we continue on to next impersonation
                        //dunno if this is right - and I'm not using it.. So.. not good
                        //TODO: Check up on this... 
                        if(i == impersonate){
                            //do nothing - we already got the msg
                        }
                        else{
                            ml = msgs.get(impersonate);
                            BigInteger b1 = (BigInteger)ml.getNextMsg(i);
                            int i1 = (Integer)ml.getNextMsg(i);
                            //one could now call reconsPublContinue, but I see no need.
                        }
                    }
                }
            }
            if(msgs != null)
                faultLocalizationContinue(msgs);
        }
        Utilities.lock.unlock();
    }

    /**
     * Takes a reconstructed value u_j and applies it when we received
     * at least T+t' (T-1)-consistent values.
     */
    public void reconsPublContinue(BigInteger u_j, int degree, int sender, int caller){
        Utilities.lock.lock();
        if(reconsPubl_Us.containsKey(sender)){
            //someone might try to cheat.. ignoring input for now
            Utilities.lock.unlock();
            error(RECONSPUBL);
            return;
        }
        if(caller == Utilities.GENTRIP){
            msgs.add(u_j, sender);
            msgs.add(degree, sender);
        }

        if(u_j.equals(BigInteger.valueOf(-1))){
            //Someone was unhappy with the private reconstruction of
            //their u_j.
            unhappyCount++;
        }
        reconsPubl_Us.put(sender, u_j);
        if(reconsPubl_Us.size() == Utilities.n_prime){
            //Now @step 4 in ReconsPubl - compute all shares which we
            //got from the input to reconsPubl
            if(unhappyCount > Utilities.t_prime){
                //Problem cannot only be with the corrupted ones. At
                //least one honest player is actually unhappy. Thus we
                //are too.
                unhappyCount = 0; //reset unhappyCounter
                setHappy(false);
                reconsPubl_Us.clear(); //reset for next time
                main.nextSegment();
                Utilities.lock.unlock();
                return;
            }
            
            Polynomial bw = sharingProtocol.BW((Utilities.T-1), reconsPubl_Us);  
            
            HashMap<Integer, BigInteger> consistentShares = new HashMap<Integer, BigInteger>();
            for(int i = 1; i <= Utilities.T; i++){
                consistentShares.put(i, (BigInteger)bw.calculateValue(BigInteger.valueOf(i)));
            }            

            BigInteger[] ss = sharingProtocol.computeSs(consistentShares, degree);

            reconsPubl_Us.clear(); //reset for next time

            switch(caller){
            case Utilities.GENTRIP:
                //We now add the generated triples to our
                //arraylist. This is done such that we can run it
                //multiple times in order to get enough triples.
                triples.addAll(genTrip.lastStep(ss));
                unhappyCount = 0; // reset for next time
                main.nextSegment();
                break;
            case Utilities.COMPPHASE:
                HashMap<Integer, BigInteger> reconstructed_d = new HashMap<Integer, BigInteger>();
                for(int i = 0; i < ss.length/2; i++){
                    reconstructed_d.put(i,ss[i]);
                }

                HashMap<Integer, BigInteger> reconstructed_e = new HashMap<Integer, BigInteger>();
                for(int i = ss.length/2; i < ss.length; i++){
                    reconstructed_e.put(i-ss.length/2, ss[i]);
                }
                //System.out.println("All e's should be the same, and is: " + ss[0]);
                HashMap<Integer, BigInteger> z_k = new HashMap<Integer, BigInteger>();
                for(int i = 0; i< reconstructed_e.size(); i++){
                    Zn zn = Utilities.zn;
                    BigInteger e = reconstructed_e.get(i);
                    BigInteger d = reconstructed_d.get(i);
                    BigInteger[] triple = triples.remove(0);
                    BigInteger a = triple[0], b = triple[1], c = triple[2];
                    BigInteger de = (BigInteger)zn.mul(e,d);
                    BigInteger db = (BigInteger)zn.mul(d,b);
                    BigInteger ea = (BigInteger)zn.mul(e,a);                        
                    BigInteger z = de.add(db).add(ea).add(c).mod(Utilities.F_big);
                    //This is the share which should be placed in the mult-gate. 
                    circuit.initMultGate(z);
                }
                //tell cc that we are done computing stuff
                cc.continueMultGates(ID);
            }
        }
        Utilities.lock.unlock();
    }

    //========== DoubleShareRandom ==========
    public void doubleShareRandom(int d1, int d2, HashMap<Integer, MessageList> msgs){
        dsr.doubleShareRandom(d1, d2, msgs);
    }

    public void receiveDoubleShare(BigInteger s1, BigInteger s2, int sender, HashMap<Integer, MessageList> messages){
        Utilities.lock.lock();
        msgs.add(s1, sender);
        msgs.add(s2, sender);
        dsr.receiveDoubleShare(s1, s2, sender, messages);
        Utilities.lock.unlock();
    }

    public void receiveCheckDoubleShare(BigInteger s1, BigInteger s2, int sender){
        Utilities.lock.lock();
        msgs.add(s1, sender);
        msgs.add(s2, sender);
        dsr.receiveCheckDoubleShare(s1, s2, sender);
        Utilities.lock.unlock();
    }

    public void doubleShare_waitForCheck(int sender){
        dsr.doubleShare_waitForCheck(sender);
    }

    //========== GenerateTriples ==========
    //convenience method for calling the non-check gen-trip 
    public void generateTriples(){
        genTrip = new GenerateTriples(this, null);
        genTrip.start();
    }
     
    public void generateTriples(HashMap<Integer, MessageList> msgs){
        genTrip = new GenerateTriples(this, msgs);
        genTrip.start();
    }

    //========== Preperation phases =======
    public void faultDetection(){
        ArrayList<Object> list = new ArrayList<Object>();
        list.add(happy);
        for(Integer i : players.keySet()){
            sendMessage(Utilities.FAULTDETECTION, list, i);
        }
    }

    public void faultDetectionReceive(boolean happy, int sender){        
        faultDetect.put(sender, happy);
        if(happy == false)
            setHappy(false);
        if(faultDetect.size() == Utilities.n_prime){
            faultDetect.clear(); //clear till next time
            //now run consensus protocol on each players happy-bit
            ArrayList<Object> list = new ArrayList<Object>();
            list.add(happy);
            for(Integer i : players.keySet()){
                sendMessage(Utilities.FAULTDETECTION_PART2, list, i);                
            }
        }
    }

    public void faultDetectionPart2(boolean happy, int sender){
        faultDetectPart2.put(sender, happy);
        if(faultDetectPart2.size() == Utilities.n_prime){
            int unhappies = 0;
            for(Integer i : faultDetectPart2.keySet()){
                if(faultDetectPart2.get(i).equals(false))
                    unhappies++;
            }
            faultDetectPart2.clear();
            if(Utilities.lock.getHoldCount() > 0)
                Utilities.lock.unlock();
            if(unhappies >= (Utilities.n_prime-Utilities.t_prime))
                //Consensus is unhappy - start fault localization
                main.returnHappyness(ID, false);
            else{
                //Consensus is happy - go to next segment
                msgs.clearMessages(); //clear messageList for next segment
                main.returnHappyness(ID, true);
            }
        }
    }

    public void faultLocalization(){
        print("Fault localization - now we're hunting the corrupted player");
        ArrayList<Object> list = new ArrayList<Object>();
        for(Integer i : players.keySet()){
            list.add(msgs);
            sendMessage(Utilities.FAULTLOCALIZATION, list, i);
            break; //only send to the one with lowest index - he is judge.
        }
    }

    int impersonate = 0;  //global variable for getting the current player whom we impersonate
    /**
     * This is a method solely for the use of the judge. When he got
     * all messageLists, he can compare every received message with
     * the messages that should have been sent computed from the
     * random choices.
     */
    public void faultLocalizationReceive(MessageList message, int sender){
        messageMap.put(sender, message);
        if(messageMap.size() == Utilities.n_prime){
            for(Integer i : messageMap.keySet()){
                //messageMap.get(i).check();
            }
            /* //This was the original proposal
            for(Integer i : messageMap.keySet()){
                impersonate = i;
                generateTriples(messageMap);
                break; //continue function takes care of the rest...
            }
            */
            
            //now we cheat a little.. starting from the back where the corrupter is.
            int lastPlayer = 1;
            for(Integer i : messageMap.keySet()){
                lastPlayer = i;
            }
            impersonate = lastPlayer;
            print("");
            print("Next subject is player " + impersonate +". Maybe this one is the corrupter");
            print("");                
            generateTriples(messageMap);         
        }
    }

    int noOfGenTrips = 1;
    public void faultLocalizationContinue(HashMap<Integer, MessageList> messages){
        if(noOfGenTrips < main.getNoOfGenTrips()){
            System.out.println("");
            System.out.println(" === Going again - Testing the generation of more triples ===");
            System.out.println("");
            noOfGenTrips++;
            generateTriples(messages);
        }
        else{
            /* //Original proposal 
            impersonate++;
            if(impersonate > Utilities.n){
                System.out.println("Done testing and checking everyone in their heads and asses"+
                                   " However, noone was found guilty.. This means an error occured somewhere.");
                messages.clear();                
                return;
            }
            else{
                if(!messages.containsKey(impersonate)){
                    impersonate++;
                }
                print("");
                print("Next subject is player " + impersonate +". Maybe this one is the corrupter");
                print("");
                for(Integer i : messages.keySet()){
                    messages.get(i).resetMessageCounter();
                }
                generateTriples(messages);    
            }
            */
            // New suggestion - starting with the ones we know are corrupted.
            impersonate--;
            if(impersonate < 1){
                System.out.println("Done testing and checking everyone in their heads and asses"+
                                   " However, noone was found guilty.. This means an error occured somewhere.");
                messages.clear();                
                return;                
            }
            else{
                if(!messages.containsKey(impersonate)){
                    impersonate--;
                }
                print("");
                print("Next subject is player " + impersonate +". Maybe this one is the corrupter");
                print("");
                for(Integer i : messages.keySet()){
                    messages.get(i).resetMessageCounter();
                }
                generateTriples(messages);    
            }
        }        
    }

    /**
     * Broadcasts the judges call that either p1 or p2 is corrupt. p1
     * claims to have send x and p2 claims to have received x_prime.
     */
    public void faultDetected(int p1, int p2, Object x, Object x_prime, int index){
        System.out.println("Following players are accused of corruption: Player "+ p1 
                           + ", Player "+p2+". Player "+p1+" should have sent "+ x
                           + ", but Player "+p2+" says he received "+ x_prime
                           + ". The received message is found at index " +index);
        ArrayList<Object> list = new ArrayList<Object>();
        list.add(p1);
        list.add(p2);
        list.add(x);
        list.add(x_prime);
        list.add(index);
        for(Integer i: players.keySet()){
            sendMessage(Utilities.ACCUSATION, list, i);
        } 
    }

    public void accusation(int p1, int p2, Object x, Object x_prime, int index){
        ArrayList<Object> list = new ArrayList<Object>();
        if(ID == p1){
            //TODO: In principle I have the knowledge of what I sent
            //myself, but my program does not store this. Therefore
            //this is a future work section - however, the judge is
            //always honest, so I just always agree.
            list.add(true);
            System.out.println("I am the accused player "+p1+", and I actually agree with the judge...");
        }
        if(ID == p2){
            //this gets the sent object to p2 from p1 at the index given
            Object question = msgs.getMsg(p1, index);
            if(question.equals(x_prime)){
                //judge is right - I agree                
                list.add(true);
                System.out.println("I am the accused player "+p2+", and I actually agree with the judge...");
            }
            else{
                list.add(false);
                System.out.println("I am the accused player, and I DO NOT agree with the judge...");
            }
        }
        if(ID == p1 || ID == p2){
            Utilities.lock.lock();
            for(Integer i : players.keySet()){
                sendMessage(Utilities.ACCUSATIONRESPONSE, list, i);
            }
            Utilities.lock.unlock();
        }
        //o/w we do nothing at the moment.
    }

    HashMap<Integer, Boolean> accusationResponse = new HashMap<Integer, Boolean>();
    public void accusationResponse(boolean response, int sender){
        accusationResponse.put(sender, response);        
        if(accusationResponse.size() == 2){
            //ready - both parties have answered
            int firstElimination = 0, secondElimination = 0;
            for(Integer i : accusationResponse.keySet()){
                if(accusationResponse.get(i) == false){
                    //P_i disagree, so we set E: {P_i, P_r}
                    int P_r = 0;
                    for(Integer j: players.keySet()){
                        P_r = j;
                        break;
                    }
                    firstElimination = players.remove(P_r);
                    secondElimination = players.remove(i);
                    if(ID == P_r || ID == i)
                        eliminated = true;
                    return;
                }
            }
            //if both agreed, we set the accused in E:
            for(Integer i: accusationResponse.keySet()){
                if(firstElimination != 0)
                    secondElimination = players.remove(i);
                else
                    firstElimination = players.remove(i);
                if(ID == i)
                    eliminated = true;
            }
            System.out.println("Removed two players: E:{P"+firstElimination+", P"+
                               secondElimination+"}. Now got a player pool size of: " + players.size());
            accusationResponse.clear(); //reset for next time...

            //reorder player ID's to get 1,...,n' not fragmented:
            int oldID = ID;
            int pID = 1;
            HashMap<Integer, Integer> tmp = new HashMap<Integer, Integer>();
            tmp.putAll(players);
            for(Integer i: tmp.keySet()){
                if(i == pID){
                    pID++;
                    continue; //do nothing - at the right pID from start
                }
                int player = players.remove(i);
                players.put(pID, player);
                if(ID == i){
                    //I moved in the line - have to reset my server
                    //accordingly However, I cannot do that until the
                    //other players reset theirs. Thus, main tells us
                    //when we can do that (via resetServer)
                    print("changed ID. from " + ID + " to "+ pID);
                    ID = pID;
                    dsr.playerID = ID;
                    this.msgs.pID = ID;
                    changedID = true;
                    server.kill();
                }
                pID++;
            }                
            //since we're done with checking stuff, we can reset our MessageList
            this.msgs.clearMessages();
            //Tell controller about who was eliminated
            main.playerElimination(oldID, firstElimination, secondElimination);
        }
    }

    boolean changedID = false;
    /**
     * Resets the server of the player if this player's ID was
     * changed. Also restarts the DoubleShareRandom in order to get
     * rid of old stuff still in the hashmaps. Also resets the
     * happy-bit.
     */
    public void resetServer(){
        happy = true;
        dsr = new DoubleShareRandom(this, sharingProtocol, M);
        if(changedID){
            server = new PlayerServer(2000+ID, this);
            server.start();
            changedID = false;
        }
        main.nextSegment(); //restart the segment
    }

    //========== Circuit stuff ============
    public void test(){
        circuit.test();
    }

    public void constGate(ConstGate g){
        circuit.addConstGate(g);
    }

    public void addGate(AddGate g){
        circuit.addAddGate(g);
        circuit.evalAddGate(g.getID());
    }

    /**
     * Gets the multiplication gate to be inserted in the local
     * circuit. Now doing calculations to make it robust.
     */
    public void multGate(ArrayList<MultGate> gates){
        if(gates.size() == 0){
            return;
        }
        HashMap<Integer, BigInteger> reconsPublList = new HashMap<Integer, BigInteger>();
        for(int i = 0; i<gates.size(); i++){
            MultGate g = gates.get(i);

            circuit.addMultGate(g);        
        
            Gate left = circuit.removeGate(g.leftType, g.leftID);
            Gate right = circuit.removeGate(g.rightType, g.rightID);
            BigInteger x = left.getShare();
            BigInteger y = right.getShare();
            
            BigInteger[] triple = triples.get(i);
            BigInteger d = x.subtract(triple[0]).mod(Utilities.F_big);
            reconsPublList.put(i,d);

            BigInteger e = y.subtract(triple[1]).mod(Utilities.F_big);
            reconsPublList.put(i+gates.size(), e);
            //for debugging..
            /*
                System.out.println("-------------- Player "+ID+" ------------");
                System.out.println("Triple is:  (" +triple[0]+", "+triple[1]+", "+triple[2]+")");
                System.out.println("Left share is: " +x);
                System.out.println("Which gives a d of: " + d);
                System.out.println("Right share is: "+y);
                System.out.println("Which gives a e of: " + e);
                System.out.println("----------------------------------");
            */
        }
        reconsPubl(Utilities.t, reconsPublList, Utilities.COMPPHASE, null);
    }

    /*
    public void outputGate(OutputGate g){
        //The ID is the player who should reconstruct private
        int ID = g.getID();
        reconsPriv(ID, Utilities.OUTPUTGATE, g);
    }
    */

    //========== set/get methods and utility=========

    public void setRandomDoubleShares(HashMap<Integer, BigInteger[]> randShares){
        randomDoubleShares = randShares;
    }

    public HashMap<Integer, BigInteger[]> getRandomDoubleShares(){
        return randomDoubleShares;
    }

    public void setHappy(boolean happy){
        this.happy = happy;
    }

    public boolean getHappy(){
        return happy;
    }

    public int getID(){
        return ID;
    }

    public int getOldID(){
        return oldID;
    }

    public BigInteger random(){
        return BigInteger.valueOf(rand.nextInt(Utilities.F)).mod(Utilities.F_big);
    }

    public void addMessage(Object o, int senderID){
        msgs.add(o, senderID);
    }

    public void addMessageRandomness(BigInteger[][] rand){
        msgs.addRandomness(rand);
    }

    public void error(int errCode){
        switch(errCode){
        case DOUBLESHARERANDOM:
            System.err.println("Someone tried to run a method outside of the "+
                               "DoubleShareRandom protocol on player "+
                               ID +". Ignoring this futile attempt of sabotage!");
            break;
        case RECONSPRIV: 
            System.err.println("Calling RECONSPRIV with an ID that was already there.");
            break;
        case RECONSPUBL:
            System.err.println("Calling RECONSPUBL with an ID that was already there.");
            break;
        }
    }

    public void print(String msg){
        if(DEBUG){
            System.out.println(msg);
        }
    }
    
    public void print_less(String msg){
        if(DEBUG){
            System.out.print(msg);
        }
    }

    public void shutdown(){
        server.kill();
        if(genTrip != null){
            genTrip.interrupt();
        }
        //interrupt();        
    }

}